#!/bin/sh
#
# VirtualBox script for BSD Media Center
#
# Copyright (c) 2009-2012, The BSDRP Development Team
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#

#Uncomment for debug
#set -x

set -eu

# Global variable
VM_NAME="BSDMC"
LOG_FILE="${HOME}/BSDMC_lab.log"

# A usefull function (from: http://code.google.com/p/sh-die/)
die() { echo -n "EXIT: " >&2; echo "$@" >&2; exit 1; }

# Check FreeBSD system pre-requise for starting virtualbox
check_system_freebsd () {
    if ! kldstat -m vboxdrv > /dev/null 2>&1; then
        echo "[WARNING] vboxdrv module not loaded ?"
    fi
}

check_system_common () {
	echo "Checking if VirtualBox installed..." >> ${LOG_FILE}

    if ! `VBoxManage -v > /dev/null 2>&1`; then
        die "[ERROR] Is VirtualBox installed ?"
    fi
	VBVERSION=`VBoxManage -v`
	VBVERSION_MAJ=`echo $VBVERSION|cut -d . -f 1`
	VBVERSION_MIN=`echo $VBVERSION|cut -d . -f 2`

	[ $VBVERSION_MAJ -lt 4 ] && die "[ERROR] Need Virtualbox 4.1 minimum"
	
	[ $VBVERSION_MAJ -eq 4 -a $VBVERSION_MIN -lt 1 ] &&
        die "[ERROR] Need Virtualbox 4.1 minimum"

	if ! `VBoxHeadless | grep -q vnc`; then
		if ! `VBoxHeadless | grep -q vrd`; then
			echo "No Virtualbox VRD/VNC support detected:"
			echo "BSDRP vga images will not be supported (only serial)"
			echo "VRDP: Supported by Virtualbox closed source release"
			echo "VNC:  Supported by FreeBSD VirtualBox-OSE (if enabled during make config)"
			exit 1
		else
			VBOX_OUTPUT="vrdp"
			VBOX_VGA=true
		fi
	else
		VBOX_OUTPUT="vnc"
		VBOX_VGA=true
    fi

}

check_system_linux () {
	if ! `grep -q vboxdrv /proc/modules`; then
		echo "[WARNING] VirtualBox module not loaded ?"
	fi
}

# Check user
check_user () {
    if ! `id ${USER} | grep -q vboxusers`; then
        die "[WARNING] Your user is not in the vboxusers group"
    fi
}

# Check filename given, and unzip it
check_image () {
    [ -f $1 ] || die "[ERROR] Can't found the file $1"

    if echo $1 | grep -q xz  >> ${LOG_FILE} 2>&1; then
        echo "xz compressed image detected, unzip it..."
		if ! which xz > /dev/null 2>&1; then
            die "[ERROR] Need xz for unxz the compressed image!"
        fi
        xz -dkf ${FILENAME} || die "[ERROR] Can't unxz image file!"

        # change FILENAME by removing the last.lzma"
        FILENAME=`echo ${FILENAME} | sed -e 's/.xz//g'`
	fi

    if ! `file -b ${FILENAME} | grep -q "boot sector"  >> ${LOG_FILE} 2>&1`; then
        die "[ERROR] Not a BSDMC image??"
    fi
	
}

# Create BSDMC VM by converting raw image disk file (given in parameter) into Virtualbox format and compress it
# This template is used only for the image disk
create_vm () {
	echo "Check if BSDRP VM exist..." >> ${LOG_FILE}
    if check_vm ${VM_NAME}; then
		echo "Found: Deleting BSDMC VM..."
        delete_vm ${VM_NAME}
    fi

	# Generate $VM_ARCH and $CONSOLE from the filename	
	parse_filename $1

    if ! VBoxManage createvm --name ${VM_NAME} --ostype $VM_ARCH --register >> ${LOG_FILE} 2>&1; then
		die "[ERROR] Can't create VM!"
	fi

	if [ -z "$RAM" ]; then
		RAM=256
	fi

	# Enabling ICH9 chipset (support 36 NIC)
    if ! VBoxManage modifyvm ${VM_NAME} --chipset ich9 --memory $RAM --vram 32 --boot1 disk --floppy disabled >> ${LOG_FILE} 2>&1; then
		die "[ERROR] Can't customize ${VM_NAME}"
	fi

    if ! VBoxManage modifyvm ${VM_NAME} --biosbootmenu disabled >> ${LOG_FILE} 2>&1; then
		die "[ERROR] Can't disable bootmenu for $1"
	fi

	echo "Add ATA controller to the VM..." >> ${LOG_FILE}
	if ! VBoxManage storagectl ${VM_NAME} --name "ATA Controller" --add ide --controller PIIX4 >> ${LOG_FILE} 2>&1; then
		die "[ERROR] Can't add ATA controller to the VM!"
	fi

	echo "Convert BSDRP image disk to VDI..." >> ${LOG_FILE}
    if ! VBoxManage convertfromraw "$1" "${WORKING_DIR}/${VM_NAME}/${VM_NAME}.vdi" >> ${LOG_FILE} 2>&1; then
		die "[ERROR] Can't convert BSDMC image disk!"
	fi

	echo "Add the VDI to the VM..." >> ${LOG_FILE}
	if ! VBoxManage storageattach ${VM_NAME} --storagectl "ATA Controller" \
    --port 0 --device 0 --type hdd \
    --medium "${WORKING_DIR}/${VM_NAME}/${VM_NAME}.vdi" >> ${LOG_FILE} 2>&1; then
		die "[ERROR] Can't add VDI to the VM!"
	fi

	echo "Compress the VDI..." >> ${LOG_FILE}
    if ! VBoxManage modifyvdi "${WORKING_DIR}/${VM_NAME}/${VM_NAME}.vdi" --compact >> ${LOG_FILE} 2>&1; then
		die "[ERROR] Can't compres the VDI!"
	fi

	if ! VBoxManage modifyvm ${VM_NAME} --uart1 0x3F8 4 --uartmode1 server /tmp/${VM_NAME}.serial >> ${LOG_FILE} 2>&1; then
        die "[ERROR] Can't configure serial port for ${VM_TPL_NAME}"
    fi

    if ! VBoxManage modifyvm ${VM_NAME} --nic1 intnet --nictype1 82540EM >> ${LOG_FILE} 2>&1; then
						die "[ERROR] Can't add NIC to VM ${VM_NAME}"
	fi

	#Save CONSOLE type to extradata
	 VBoxManage setextradata ${VM_NAME} Console VGA
}

# Check if VM allready exist
# Return 0 (true) if exist
# Returen 1 (false) if doesn't exist
check_vm () {
	echo "Check if $1 exist..." >> ${LOG_FILE}
   if `VBoxManage showvminfo $1 > /dev/null 2>&1`; then
		echo "Found it..." >> ${LOG_FILE}
        return 0 # true
   else
		echo "Didn't found it..." >> ${LOG_FILE}
        return 1 # false
   fi
}

# Parse filename for detecting ARCH and console
parse_filename () {
    VM_ARCH=0
    if echo "$1" | grep -q "amd64"; then
        VM_ARCH="FreeBSD_64"
        echo "x86-64 image"
		
    fi
    if echo "$1" | grep -q "i386"; then
        VM_ARCH="FreeBSD"
        echo "i386 image"
    fi
    [ "$VM_ARCH" = "0" ] && die "[ERROR] Can't deduce arch type from filename"
    
	VM_OUTPUT=0
    SERIAL=false
    echo "vga image"
	if ! $VBOX_VGA; then
		die "[ERROR] You can't use BSDRP vga release with a Virtualbox that didn't support RDP or VNC"
	fi

}

# Start each vm
start_lab () {
    local i=1
	echo "Start the lab..."
	if [ "${VBOX_OUTPUT}" = "vnc" ]; then
   		nohup VBoxHeadless --vnc --${VBOX_OUTPUT}port 590${i} --startvm ${VM_NAME} >> ${LOG_FILE} 2>&1 &
	else
		nohup VBoxHeadless --${VBOX_OUTPUT} on --${VBOX_OUTPUT}port 590${i} --startvm ${VM_NAME} >> ${LOG_FILE} 2>&1 &
	fi
	echo "BSDMC started, console acces:"
	echo "graphical: Use a ${VBOX_OUTPUT} client on port 590${i}"
	echo "text: socat unix-connect:/tmp/${VM_NAME}.serial stdio,raw,echo=0,icanon=0"

}

# Delete VM
# $1: name of the VM
delete_vm () {
    if [ -z "$1" ]; then
        echo "BUG: In delete_vm (), no argument given"
        exit 1
    fi
	if check_vm $1; then
    	echo "Delete VM $1" >> ${LOG_FILE} 2>&1
   		echo "Delete VM $1" 
    	if ! VBoxManage unregistervm $1 --delete >> ${LOG_FILE} 2>&1; then
			die "[ERROR] Can't delete VM $1, Check ${LOG_FILE}."
		fi
	fi

	#Some times, templates is deletet, but there is some file that was not deletet
	if ! check_vm $1; then
    	if [ -d "${WORKING_DIR}/$1" ]; then
			echo "[WARNING] Force deleting directory for $1"
			rm -rf "${WORKING_DIR}/$1"
    	fi
	fi

}

# Stop All VM
stop_all_vm () {
    local LIST_RUNNING_VM=""
	LIST_RUNNING_VM=`VBoxManage list runningvms | grep BSDMC | cut -d "\"" -f 2`
    #Enter the main loop for each VM
	for i in ${LIST_RUNNING_VM}; do
		echo "Stopping $i..."
		if ! VBoxManage controlvm $i poweroff >> ${LOG_FILE} 2>&1; then
			echo "[WARNING] Can't poweroff $i"
		fi
    done
}

# Get Virtualbox hostonly adapter informatiom
vbox_hostonly () {
	if ! `VBoxManage list bridgedifs | grep -m 1 "^Name:"  >> ${LOG_FILE} 2>&1`; then
        echo "ERROR: Not VBox bridge NIC, you need to create one:"
		echo "VBoxManage hostonlyif create"
		echo "VBoxManage hostonlyif ipconfig vboxnet0 --ip 192.168.1.30 --netmask 255.255.255.0"
        exit 1
    fi

	BRIDGE_NIC_NAME=`VBoxManage list bridgedifs | grep -m 1 "^Name:" | cut -d ':' -f 2 | sed 's/^[ \t]*//;s/[ \t]*$//'`
}

usage () {
        (
        echo "Usage: $0 [-hds] [-i BSDMC_file.img]"
        echo "  -i filename     BSDMC image file name (to be used the first time only)"
        echo "  -d              Delete BSDMC VM and disks"
		echo "  -m              RAM (in MB) for each VM (default: 192)"
        echo "  -h              Display this help"
        echo "  -s              Stop all VM"
        echo ""
        ) 1>&2
        exit 2
}

###############
# Main script #
###############

### Parse argument

#set +e
args=`getopt i:dhm:s $*`
if [ $? -ne 0 ] ; then
        usage
        exit 2
fi
#set -e

LAN=""
FILENAME=""
RAM=""

echo "BSD Router Project (http://bsdrp.net) - VirtualBox lab script"

echo "BSD Router Project (http://bsdrp.net) - Virtualbox lab script, log file" > ${LOG_FILE}

OS_DETECTED=`uname -s`

check_system_common

case "$OS_DETECTED" in
    "FreeBSD")
        check_system_freebsd
        break
        ;;
    "Linux")
        check_system_linux
        break
        ;;
    *)
        echo "ERROR: This script doesn't support $OS_DETECTED"
        exit 1
        ;;
esac

check_user

WORKING_DIR=`VBoxManage list systemproperties | grep "Default machine folder" | cut -d ":" -f 2 | tr -s " " | sed '1s/^.//'`
MAX_NIC=`VBoxManage list systemproperties | grep "Maximum PIIX3 Network Adapter count" | cut -d ":" -f 2 | tr -s " " | sed '1s/^.//'`

set -- $args
for i
do
        case "$i" 
        in
        -d)
                delete_vm ${VM_NAME}
				exit 0
                ;;
        -h)
                usage
                ;;
        -i)
                FILENAME="$2"
                shift
                shift
                ;;
		-m)
				RAM="$2"
				shift
				shift
				;;
        -s)
                stop_all_vm
				exit 0
                ;;
        --)
                shift
                break
        esac
done

if [ $# -gt 0 ] ; then
    echo "$0: Extraneous arguments supplied"
    usage
fi

HOSTONLY_NIC=false
if ($HOSTONLY_NIC); then
	vbox_hostonly
fi

if ! check_vm ${VM_NAME}; then
	if [ "$FILENAME" != "" ]; then
    	check_image ${FILENAME}
    	#Note: check_image will change the $FILENAME variable...
    	create_vm ${FILENAME}
	else
        die "[ERROR] You need to enter an image filename for creating the VM."
	fi
fi

start_lab
